﻿using System;
using System.Drawing;
using System.Windows.Forms;
using System.Globalization;

namespace DotNet.Common
{
    internal class HexByteControl : TextBox
    {
        public event EventHandler<FocusMoveEventArgs> FocusAction;

        public override Size MinimumSize
        {
            get
            {
                Size minimumSize;
                using (Graphics g = Graphics.FromHwnd(Handle))
                {
                    minimumSize = TextRenderer.MeasureText(g,
                       "FF", Font, Size,
                       TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.NoPadding);
                }

                return minimumSize;
            }
        }

        public byte Value
        {
            get
            {
                byte result;
                byte.TryParse(base.Text, NumberStyles.HexNumber, null, out result);
                return result;
            }
            set
            {
                base.Text = value.ToString("X2");
            }
        }

        int _index;
        public int Index
        {
            get { return _index; }
            set { _index = value; }
        }

        const int _maxLength = 2;
        public HexByteControl()
        {
            base.BorderStyle = BorderStyle.None;
            base.MaxLength = HexByteControl._maxLength;
            base.Size = MinimumSize;
            base.TabStop = false;
            base.TextAlign = HorizontalAlignment.Center;
        }

        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            base.Size = this.MinimumSize;
        }

        protected override void OnSizeChanged(System.EventArgs e)
        {
            base.OnSizeChanged(e);
            base.Size = this.MinimumSize;
        }

        protected override void OnParentForeColorChanged(EventArgs e)
        {
            base.OnParentForeColorChanged(e);
            base.ForeColor = Parent.ForeColor;
        }

        protected override void OnParentBackColorChanged(EventArgs e)
        {
            base.OnParentBackColorChanged(e);
            base.BackColor = Parent.BackColor;
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            base.SelectAll();
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            switch (e.KeyData)
            {
                case Keys.Left:
                case Keys.Back:
                    if (base.SelectionStart == 0 && base.SelectionLength == 0)
                    {
                        FocusMove(MoveAction.Left);
                    }
                    break;
                case Keys.Right:
                    if (base.SelectionStart == base.TextLength && base.SelectionLength == 0)
                    {
                        FocusMove(MoveAction.Right);
                    }
                    break;
                case Keys.Home:
                    FocusMove(MoveAction.Home);
                    break;
                case Keys.End:
                    FocusMove(MoveAction.End);
                    break;
            }
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);

            if (this.IsHexKey(e.KeyChar))
            {
                e.KeyChar = char.ToUpper(e.KeyChar);
                if (base.SelectionStart == HexByteControl._maxLength - 1)
                {
                    FocusMove(MoveAction.Right);
                }
            }
            else if (!IsCtrlKey(e.KeyChar))
            {
                e.Handled = true;
            }
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);

            byte result;
            if (byte.TryParse(base.Text, NumberStyles.HexNumber, null, out result))
            {
                base.Text = base.Text.ToUpper();
            }
            else
            {
                base.Clear();
            }
        }

        private bool IsHexKey(char key)
        {
            return (key >= '0' && key <= '9') || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F');
        }

        private bool IsCtrlKey(char key)
        { 
            const ushort _ctrl_c = 'c' - 'a' + 1;
            const ushort _ctrl_v = 'v' - 'a' + 1;
            const ushort _ctrl_z = 'z' - 'a' + 1;
            const ushort _ctrl_x = 'x' - 'a' + 1;

            return (key == '\b' || key == _ctrl_c || key == _ctrl_v || key == _ctrl_z || key == _ctrl_x);
        }

        private void FocusMove(MoveAction action)
        {
            if (FocusAction != null)
            {
                FocusAction(this, new FocusMoveEventArgs(this.Index, action));
            }
        }
    }

    public enum MoveAction
    {
        Left,Right,Home,End
    }

    public class FocusMoveEventArgs : EventArgs
    {
        int _index;
        public int Index
        {
          get { return _index; }
        }

        MoveAction _action;
        public MoveAction Action
        {
            get { return _action; }
        }

        public FocusMoveEventArgs(int index,MoveAction action)
        {
            _index = index;
            _action = action;
        }
    }
}
